#define STRINGVIEW_NAMESPACE 
#define STRINGVIEW_NAMESPACE_BEGIN
#define STRINGVIEW_NAMESPACE_END

#include <cstring>
#include <iostream>
#include <cassert>

#include "string_view.hpp"

#define ASSERT(x) assert(x)

#if STRINGVIEW_CPLUSPLUS == 201103L
#  define CPLUSPLUS_STR "C++11"
#elif STRINGVIEW_CPLUSPLUS == 201402L
#  define CPLUSPLUS_STR "C++14"
#elif STRINGVIEW_CPLUSPLUS == 201703L
#  define CPLUSPLUS_STR "C++17"
#elif STRINGVIEW_CPLUSPLUS == 201709L
#  define CPLUSPLUS_STR "C++2a"
#elif STRINGVIEW_CPLUSPLUS == 202002L
#  define CPLUSPLUS_STR "C++20"
#else
#  define CPLUSPLUS_STR "C++20 later"
#endif

#define EXPECT_TRUE(x)  ASSERT((x))
#define EXPECT_GT(x, y) ASSERT((x) > (y))
#define EXPECT_EQ(x, y) ASSERT((x) == (y))
#define EXPECT_FALSE(x) ASSERT(!(x))
#define EXPECT_NO_FATAL_FAILURE(x) (x)

void test();

int main(int argc, char* argv[])
{
    std::cout << "string_view test for " << CPLUSPLUS_STR << ": " << STRINGVIEW_CPLUSPLUS << std::endl;

    test();

    return 0;
}

using StringView = string_view;

void test()
{
    StringView sv;
    EXPECT_TRUE(sv.empty());

    StringView sv1("");
    EXPECT_TRUE(sv1.empty());
    EXPECT_EQ(sv1.begin(), sv1.end());
    EXPECT_EQ(sv1.cbegin(), sv1.cend());
    EXPECT_EQ(sv1.rbegin(), sv1.rend());
    EXPECT_EQ(sv1.crbegin(), sv1.crend());

    EXPECT_GT(sv1.max_size(), 0);

    StringView sv2("1234");
    StringView sv3("1234", 4);
    StringView sv4("12345", 4);
    EXPECT_TRUE(sv2 == sv3);
    EXPECT_TRUE(sv2 == sv4);
    EXPECT_TRUE(sv3 == sv4);
    EXPECT_TRUE(sv4.length() == 4);
    EXPECT_TRUE(sv4.size() == 4);

    StringView sv5(sv4);
    EXPECT_TRUE(sv5 == sv4);

    StringView sv6 = sv4;
    EXPECT_TRUE(sv6 == sv4);

    EXPECT_EQ(sv2[0], '1');
    EXPECT_EQ(sv2[1], '2');
    EXPECT_EQ(sv2.at(0), '1');
    EXPECT_EQ(sv2.at(1), '2');
    try {
        sv2.at(10000);
    } catch(const std::exception& e) {
        // ...
    }
    
    EXPECT_EQ(sv2.front(), sv2[0]);
    EXPECT_EQ(sv2.back(), *sv2.rbegin());
    EXPECT_NO_FATAL_FAILURE(sv2.data());
    EXPECT_EQ(sv2.size(), sv2.length());
    EXPECT_FALSE(sv2.empty());

    constexpr const char* p = "123456";
    StringView sv7("123456");
    sv7.remove_prefix(1);
    EXPECT_EQ(sv7, StringView(p + 1));
    sv7.remove_suffix(2);
    EXPECT_EQ(sv7, StringView("234"));
    sv7.swap(sv);
    sv7.swap(sv);

    StringView sv8("123");
    char buff[12] = { 0 };
    sv8.copy(buff, sv8.size());
    EXPECT_EQ(sv8, StringView(buff));

    try {
        sv8.copy(buff, sv8.size(), sv8.size() + 1);
        sv8.substr(sv8.size() + 1);
    } catch (...) {
        // ....
    }

    StringView sv9 = sv8.substr(1);
    EXPECT_EQ(sv9, StringView("23"));

    StringView sv10 = sv8.substr(1, 1);
    EXPECT_EQ(sv10, StringView("2"));

    StringView sv11("123");
    EXPECT_TRUE(sv11.compare(StringView("1")) > 0);
    EXPECT_TRUE(sv11.compare(StringView(sv11)) == 0);
    EXPECT_TRUE(sv11.compare(StringView("234")) < 0);
    EXPECT_TRUE(sv11.compare(0, 2, StringView("12")) == 0);
    EXPECT_TRUE(sv11.compare(0, 2, StringView("123"), 0, 2) == 0);
    EXPECT_TRUE(sv11.compare(sv11.data()) == 0);
    EXPECT_TRUE(sv11.compare(0, 2, "12") == 0);
    EXPECT_TRUE(sv11.compare(0, 2, "123", 2) == 0);

    EXPECT_TRUE(sv11.starts_with("12"));
    EXPECT_TRUE(sv11.starts_with('1'));
    EXPECT_FALSE(sv11.starts_with('2'));
    EXPECT_FALSE(sv11.starts_with(StringView("1234")));

    EXPECT_TRUE(sv11.ends_with("23"));
    EXPECT_TRUE(sv11.ends_with('3'));
    EXPECT_FALSE(sv11.ends_with('2'));
    EXPECT_FALSE(sv11.ends_with(StringView("12")));

    EXPECT_TRUE(sv11.contains('2'));
    EXPECT_FALSE(sv11.contains('9'));
    EXPECT_TRUE(sv11.contains("23"));
    EXPECT_FALSE(sv11.contains(StringView("233")));

    EXPECT_EQ(1, sv11.find('2'));
    EXPECT_EQ(StringView::npos, sv11.find('2', sv11.size()));
    EXPECT_EQ(StringView::npos, sv11.find('9'));

    EXPECT_EQ(1, sv11.rfind('2'));
    EXPECT_EQ(StringView::npos, sv11.rfind('2', 0));
    EXPECT_EQ(StringView::npos, sv11.rfind('9'));

    EXPECT_EQ(0, sv11.find(StringView("12")));
    EXPECT_EQ(StringView::npos, sv11.find(StringView()));
    EXPECT_EQ(StringView::npos, sv11.find(StringView("123456")));
    EXPECT_EQ(StringView::npos, sv11.find(StringView("56")));
    EXPECT_EQ(StringView::npos, sv11.find(StringView("12"), sv11.size()));
    EXPECT_EQ(StringView::npos, sv11.find(StringView("99")));
    EXPECT_EQ(2, sv11.find(StringView("3"), 1));

    EXPECT_EQ(0, sv11.rfind(StringView("12")));
    EXPECT_EQ(3, sv11.rfind(StringView())); // an empty substr is always found
    EXPECT_EQ(StringView::npos, sv11.rfind(StringView("123456")));
    EXPECT_EQ(StringView::npos, sv11.rfind(StringView("56")));
    EXPECT_EQ(0, sv11.rfind(StringView("12"), sv11.size()));
    EXPECT_EQ(StringView::npos, sv11.rfind(StringView("3"), 1));

    EXPECT_EQ(0, sv11.find("12"));
    EXPECT_EQ(StringView::npos, sv11.find("12", 1));
    EXPECT_EQ(StringView::npos, sv11.find("12", 1, 2));

    EXPECT_EQ(0, sv11.rfind("12"));
    EXPECT_EQ(StringView::npos, sv11.rfind("32", 1));
    EXPECT_EQ(1, sv11.rfind("23", 1, 2));

    StringView sv12("1234563412");
    EXPECT_EQ(sv12.find_first_of('0'), StringView::npos);
    EXPECT_EQ(sv12.find_first_of('0', 2), StringView::npos);
    EXPECT_EQ(sv12.find_first_of('2'), 1);
    EXPECT_EQ(sv12.find_first_of('2', 2), 9);

    EXPECT_EQ(sv12.find_first_of(StringView("abcd")), StringView::npos);
    EXPECT_EQ(sv12.find_first_of(StringView("34")), 2);
    EXPECT_EQ(sv12.find_first_of(StringView("34"), 4), 6);
    EXPECT_EQ(sv12.find_first_of(StringView("abcd"), 4), StringView::npos);
    EXPECT_EQ(sv12.find_first_of(StringView()), StringView::npos);

    EXPECT_EQ(sv12.find_first_of("abcd"), StringView::npos);
    EXPECT_EQ(sv12.find_first_of("34"), 2);
    EXPECT_EQ(sv12.find_first_of("34", 4), 6);
    EXPECT_EQ(sv12.find_first_of("abcd", 4), StringView::npos);

    EXPECT_EQ(sv12.find_last_of('0'), StringView::npos);
    EXPECT_EQ(sv12.find_last_of('0', 2), StringView::npos);
    EXPECT_EQ(sv12.find_last_of('2'), 9);
    EXPECT_EQ(sv12.find_last_of('2', 2), 1);

    EXPECT_EQ(sv12.find_last_of(StringView("abcd")), StringView::npos);
    EXPECT_EQ(sv12.find_last_of(StringView("34")), 7);
    EXPECT_EQ(sv12.find_last_of(StringView("34", 2)), 7);
    EXPECT_EQ(sv12.find_last_of(StringView("abcd"), 4), StringView::npos);
    EXPECT_EQ(sv12.find_last_of(StringView()), StringView::npos);

    EXPECT_EQ(sv12.find_last_of("abcd"), StringView::npos);
    EXPECT_EQ(sv12.find_last_of("34"), 7);
    EXPECT_EQ(sv12.find_last_of("34", 4), 3);
    EXPECT_EQ(sv12.find_last_of("abcd", 4), StringView::npos);

    EXPECT_EQ(sv12.find_first_not_of('1'), 1);
    EXPECT_EQ(sv12.find_first_not_of('2', 1), 2);
    EXPECT_EQ(sv12.find_first_not_of(StringView("12")), 2);
    EXPECT_EQ(sv12.find_first_not_of(StringView("12"), 1), 2);
    EXPECT_EQ(sv12.find_first_not_of(StringView("44")), 0);
    EXPECT_EQ(sv12.find_first_not_of(StringView("44"), 1), 1);
    EXPECT_EQ(sv12.find_first_not_of("12"), 2);
    EXPECT_EQ(sv12.find_first_not_of("12", 1), 2);
    EXPECT_EQ(sv12.find_first_not_of("123", 1, 2), 2);
    EXPECT_EQ(sv12.find_first_not_of("44"), 0);
    EXPECT_EQ(sv12.find_first_not_of("44", 1), 1);
    EXPECT_EQ(sv12.find_first_not_of(StringView("44"), sv12.size() + 1), StringView::npos);
    StringView empty_sv;
    EXPECT_EQ(empty_sv.find_first_not_of('c'), StringView::npos);

    EXPECT_EQ(sv12.find_last_not_of('1'), 9);
    EXPECT_EQ(sv12.find_last_not_of('2', 1), 0);
    EXPECT_EQ(sv12.find_last_not_of(StringView("12")), 7);
    EXPECT_EQ(sv12.find_last_not_of(StringView("12"), 1), StringView::npos);
    EXPECT_EQ(sv12.find_last_not_of(StringView("44")), 9);
    EXPECT_EQ(sv12.find_last_not_of(StringView("44"), 1), 1);
    EXPECT_EQ(sv12.find_last_not_of("12"), 7);
    EXPECT_EQ(sv12.find_last_not_of("12", 1), StringView::npos);
    EXPECT_EQ(sv12.find_last_not_of("123", 1, 2), StringView::npos);
    EXPECT_EQ(sv12.find_last_not_of("44"), 9);
    EXPECT_EQ(sv12.find_last_not_of("44", 1), 1);
    EXPECT_EQ(empty_sv.find_last_not_of('c'), StringView::npos);

    std::cout << sv12 << std::endl;
    std::cout << StringView() << std::endl;

    std::cout.width(10);
    std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
    std::cout << std::left << StringView("123") << std::endl;

    {
        StringView s1("1");
        StringView s2("12");
        EXPECT_FALSE(s1 == s2);

        try {
             s1.substr(2);
        } catch (...) {
            // ...
        }
    }
}